From f2770b31bee2bbb4160b474866076e13d17b9be5 Mon Sep 17 00:00:00 2001 From: Tomas Sedovic Date: Mon, 27 Oct 2014 22:49:20 +0100 Subject: [PATCH] Add `--name` and `--example` to cargo run This lets us compile and run examples using `cargo run --example NAME`. Selecting the other binary targets is now done using the `--name` flag. `cargo run` falls back to the old behaviour (running the only bin target in the project, failing if there are more) in neither `--name` nor `--example` are present. Closes #538 --- src/bin/run.rs | 36 +++++++++-- src/cargo/ops/cargo_run.rs | 18 ++++-- tests/test_cargo_run.rs | 123 ++++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 10 deletions(-) diff --git a/src/bin/run.rs b/src/bin/run.rs index 3257e110f..efdf78fd2 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -2,11 +2,14 @@ use std::io::process::ExitStatus; use cargo::ops; use cargo::core::{MultiShell}; -use cargo::util::{CliResult, CliError}; +use cargo::core::manifest::{BinTarget, ExampleTarget}; +use cargo::util::{CliResult, CliError, human}; use cargo::util::important_paths::{find_root_manifest_for_cwd}; #[deriving(Decodable)] struct Options { + flag_name: Option, + flag_example: Option, flag_jobs: Option, flag_features: Vec, flag_no_default_features: bool, @@ -25,6 +28,8 @@ Usage: Options: -h, --help Print this message + --name NAME Name of the bin target to run + --example NAME Name of the example target to run -j N, --jobs N The number of jobs to run in parallel --release Build artifacts in release mode, with optimizations --features FEATURES Space-separated list of features to also build @@ -33,6 +38,11 @@ Options: --manifest-path PATH Path to the manifest to execute -v, --verbose Use verbose output +If neither `--name` or `--example` are given, then if the project only has one +bin target it will be run. Otherwise `--name` specifies the bin target to run, +and `--example` specifies the example target to run. At most one of `--name` or +`--example` can be provided. + All of the trailing arguments are passed as to the binary to run. "; @@ -40,8 +50,16 @@ pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult shell.set_verbose(options.flag_verbose); let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path)); + let env = if options.flag_example.is_some() { + "test" + } else if options.flag_release { + "release" + } else { + "compile" + }; + let mut compile_opts = ops::CompileOptions { - env: if options.flag_release { "release" } else { "compile" }, + env: env, shell: shell, jobs: options.flag_jobs, target: options.flag_target.as_ref().map(|t| t.as_slice()), @@ -51,7 +69,18 @@ pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult spec: None, }; - let err = try!(ops::run(&root, &mut compile_opts, + let (target_kind, name) = match (options.flag_name, options.flag_example) { + (Some(bin), None) => (BinTarget, Some(bin)), + (None, Some(example)) => (ExampleTarget, Some(example)), + (None, None) => (BinTarget, None), + (Some(_), Some(_)) => return Err(CliError::from_boxed( + human("specify either `--name` or `--example`, not both"), 1)), + }; + + let err = try!(ops::run(&root, + target_kind, + name, + &mut compile_opts, options.arg_args.as_slice()).map_err(|err| { CliError::from_boxed(err, 101) })); @@ -65,4 +94,3 @@ pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult } } } - diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 16fa41b1a..bd1531491 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -2,10 +2,13 @@ use std::os; use ops; use util::{CargoResult, human, process, ProcessError, Require}; +use core::manifest::{TargetKind, LibTarget, BinTarget, ExampleTarget}; use core::source::Source; use sources::PathSource; pub fn run(manifest_path: &Path, + target_kind: TargetKind, + name: Option, options: &mut ops::CompileOptions, args: &[String]) -> CargoResult> { let mut src = try!(PathSource::for_path(&manifest_path.dir_path())); @@ -13,14 +16,21 @@ pub fn run(manifest_path: &Path, let root = try!(src.get_root_package()); let env = options.env; let mut bins = root.get_manifest().get_targets().iter().filter(|a| { - a.is_bin() && a.get_profile().get_env() == env + let matches_kind = match target_kind { + BinTarget => a.is_bin(), + ExampleTarget => a.is_example(), + LibTarget(_) => false, + }; + let matches_name = name.as_ref().map_or(true, |n| n.as_slice() == a.get_name()); + matches_kind && matches_name && a.get_profile().get_env() == env }); let bin = try!(bins.next().require(|| { human("a bin target must be available for `cargo run`") })); match bins.next() { - Some(..) => return Err(human("`cargo run` requires that a project only \ - have one executable")), + Some(..) => return Err( + human("`cargo run` requires that a project only have one executable. \ + Use the `--name` option to specify which one to run")), None => {} } @@ -28,7 +38,7 @@ pub fn run(manifest_path: &Path, let dst = manifest_path.dir_path().join("target"); let dst = match options.target { Some(target) => dst.join(target), - None => dst, + None => if bin.is_example() { dst.join("examples") } else { dst }, }; let exe = match bin.get_profile().get_dest() { Some(s) => dst.join(s).join(bin.get_name()), diff --git a/tests/test_cargo_run.rs b/tests/test_cargo_run.rs index f05aac810..97fc5e31c 100644 --- a/tests/test_cargo_run.rs +++ b/tests/test_cargo_run.rs @@ -1,6 +1,6 @@ use std::path; -use support::{project, execs, path2url}; +use support::{project, cargo_dir, execs, path2url}; use support::{COMPILING, RUNNING}; use hamcrest::{assert_that, existing_file}; @@ -98,7 +98,126 @@ test!(too_many_bins { assert_that(p.cargo_process("run"), execs().with_status(101) .with_stderr("`cargo run` requires that a project only \ - have one executable\n")); + have one executable. Use the `--name` option \ + to specify which one to run\n")); +}) + +test!(specify_name { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#" + extern crate foo; + fn main() { println!("hello a.rs"); } + "#) + .file("src/bin/b.rs", r#" + extern crate foo; + fn main() { println!("hello b.rs"); } + "#); + + assert_that(p.cargo_process("run").arg("--name").arg("a"), + execs().with_status(0).with_stdout(format!("\ +{compiling} foo v0.0.1 ({dir}) +{running} `target{sep}a` +hello a.rs +", + compiling = COMPILING, + running = RUNNING, + dir = path2url(p.root()), + sep = path::SEP).as_slice())); + + assert_that(p.process(cargo_dir().join("cargo")).arg("run").arg("--name").arg("b"), + execs().with_status(0).with_stdout(format!("\ +{running} `target{sep}b` +hello b.rs +", + running = RUNNING, + sep = path::SEP).as_slice())); +}) + +test!(run_example { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("examples/a.rs", r#" + fn main() { println!("example"); } + "#) + .file("src/bin/a.rs", r#" + fn main() { println!("bin"); } + "#); + + assert_that(p.cargo_process("run").arg("--example").arg("a"), + execs().with_status(0).with_stdout(format!("\ +{compiling} foo v0.0.1 ({dir}) +{running} `target{sep}examples{sep}a` +example +", + compiling = COMPILING, + running = RUNNING, + dir = path2url(p.root()), + sep = path::SEP).as_slice())); +}) + +test!(either_name_or_example { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/a.rs", r#" + fn main() { println!("hello a.rs"); } + "#) + .file("examples/b.rs", r#" + fn main() { println!("hello b.rs"); } + "#); + + assert_that(p.cargo_process("run").arg("--name").arg("a").arg("--example").arg("b"), + execs().with_status(1) + .with_stderr("specify either `--name` or `--example`, \ + not both")); +}) + +test!(one_bin_multiple_examples { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/main.rs", r#" + fn main() { println!("hello main.rs"); } + "#) + .file("examples/a.rs", r#" + fn main() { println!("hello a.rs"); } + "#) + .file("examples/b.rs", r#" + fn main() { println!("hello b.rs"); } + "#); + + assert_that(p.cargo_process("run"), + execs().with_status(0).with_stdout(format!("\ +{compiling} foo v0.0.1 ({dir}) +{running} `target{sep}main` +hello main.rs +", + compiling = COMPILING, + running = RUNNING, + dir = path2url(p.root()), + sep = path::SEP).as_slice())); }) test!(run_dylib_dep { -- 2.30.2